package dev.hxliu.cn.util;

import dev.hxliu.cn.inject.InjectMethod;
import dev.hxliu.cn.inject.InjectMethodEnum;
import org.springframework.core.ResolvableType;

import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.util.function.Function;
import java.util.function.UnaryOperator;


public class PhoneMaskingInject implements InjectMethod.InjectMethodArgColumn<String> {
    @Override
    public UnaryOperator<String> inject() {
        return DataMaskingUtils::phoneMasking;
    }


    /**
     * 句柄调用测试
     */
    public static void main(String[] args) throws Throwable {
        MethodHandle methodHandle1 = methodHandle(PhoneMaskingInject.class, new PhoneMaskingInject());
        System.out.println(methodHandle1.invoke("17674777903"));

        MethodHandle methodHandle2 = methodHandle(InjectMethodEnum.NOW.class,  new InjectMethodEnum.NOW());
        System.out.println(methodHandle2.invoke());
        MethodHandle methodHandle3 = methodHandle(TO_STR.class,  new TO_STR());
        System.out.println(methodHandle3.invoke(3L));

        {
            long start = System.currentTimeMillis();
        for (long i= 10000000000L;i<10100000000L;i++){
            methodHandle1.invoke(String.valueOf(i));
        }
            long end = System.currentTimeMillis();
            System.out.println(end - start);
        }
        {
            long start = System.currentTimeMillis();
        for (long i= 10000000000L;i<10100000000L;i++){
            DataMaskingUtils.phoneMasking(String.valueOf(i));
        }
            long end = System.currentTimeMillis();
            System.out.println(end - start);
        }
    }

    public static class  TO_STR implements InjectMethodArgObject<Long,String>{

        @Override
        public Function<Long,String> inject() {
            return (Object::toString);
        }
    }
//    // java8 获取全权限Lookup的通用方法
//    public static MethodHandles.Lookup getFullAccessLookup() throws Exception {
//        Field field = MethodHandles.Lookup.class.getDeclaredField("IMPL_LOOKUP");
//        field.setAccessible(true);
//        return (MethodHandles.Lookup) field.get(null);
//    }

    public static MethodHandle methodHandle(Class<? extends InjectMethod<?>> supplier, InjectMethod<?> injectMethod) throws Throwable {
        MethodHandles.Lookup lookup = MethodHandles.lookup();
        ResolvableType resolvableType = ResolvableType.forClass(supplier);
        ResolvableType injectMethodType = resolvableType.as(InjectMethod.class);
        ResolvableType genericType = injectMethodType.getGeneric();

        Class<?> rawClass = genericType.getRawClass();
        if (rawClass != null) {
            MethodType mt = MethodType.methodType(rawClass);
            MethodHandle mh = lookup.findVirtual(supplier, "inject", mt);
            if (UnaryOperator.class.isAssignableFrom(rawClass)) {
                MethodHandle methodHandle = mh.bindTo(injectMethod);
                Object invoke = methodHandle.invoke();
                MethodType applyType = MethodType.methodType(Object.class, Object.class);
                return lookup.bind(invoke, "apply", applyType);

            }else if (Function.class.isAssignableFrom(rawClass)) {
                MethodHandle methodHandle = mh.bindTo(injectMethod);
                Object invoke = methodHandle.invoke();
                MethodType applyType = MethodType.methodType(Object.class, Object.class);
                return lookup.bind(invoke, "apply", applyType);
            }else{
                return mh.bindTo(injectMethod);
            }
        }
        return null;
    }
}
